package com.example.instrumentation

import org.gradle.api.DefaultTask
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.Deflater


/**
 * create by liaohailong
 * 2025/10/31 23:46
 * desc:
 */
abstract class LogTransformTask : DefaultTask() {

    @get:InputFiles
    abstract val inputJars: ListProperty<RegularFile>

    @get:InputFiles
    abstract val inputDirectories: ListProperty<Directory>

    @get:OutputFile
    abstract val transformOutput: RegularFileProperty

    @TaskAction
    fun taskAction() {
        log("taskAction start")
        val outputFile = transformOutput.get().asFile
        if (outputFile.exists()) outputFile.delete() // clear cache
        outputFile.parentFile.mkdirs()
        log("clear cache: ${outputFile.absolutePath}")

        outputFile.outputStream().use { fos ->
            JarOutputStream(fos).use { jos ->
                // this is the fastest mode
                jos.setLevel(Deflater.NO_COMPRESSION)

                // let's go
                transform(jos)
            }
        }

        val outputFilepath = transformOutput.get().asFile.absolutePath
        log("outputFilepath $outputFilepath")
        log("taskAction over")
    }

    private fun transform(jos: JarOutputStream) {
        val jars = inputJars.get() // jars from project, jars from 3rd part aar(local + cloud maven)
        val dirs = inputDirectories.get()

        // handle jars firstly
        log("handle jars start")
        jars.forEach { rf ->
            val file = rf.asFile
            JarFile(file).use { jf ->
                // a jar file contains a lot of classes
                jf.entries().iterator().forEach { entry ->
                    val entryName = entry.name
                    if (entryName.endsWith(".class")
                        && entryName.endsWith("module-info.class").not()
                    ) {
                        val bytes = jf.getInputStream(entry).use { it.readBytes() }
                        jos.putNextEntry(entry)
                        jos.write(bytes)
                        jos.closeEntry()

                        log("[jar] found ${entry.name}")
                    }
                }
            }
        }
        log("handle jars over")

        // handle directories that all original sources(java+kotlin) here
        log("handle dirs start")
        dirs.forEach { dir ->
            val rootDir = dir.asFile
            val rootUri = rootDir.toURI()

            // traversal all files in all dirs, and put them into output which is a Jar
            rootDir.walk()
                .filter { it.name != "META-INF" }
                .filter { it.isFile }
                .forEach { file ->
                    val entryName = rootUri.relativize(file.toURI()).path
                        .replace(File.separatorChar, '/')
                    val entry = JarEntry(entryName)
                    val bytes = file.readBytes()
                    jos.putNextEntry(entry)
                    jos.write(bytes)
                    jos.closeEntry()

                    log("[dir] found ${entry.name}")
                }
        }
        log("handle dirs over")

    }

    private fun log(msg: String) {
        println("LogTransformTask: $msg")
    }

}